package com.ecoolex.ying.space.core.manager;

import com.alibaba.fastjson.JSON;
import com.ecoolex.framework.common.enums.ResultCode;
import com.ecoolex.framework.common.pojo.Page;
import com.ecoolex.framework.common.pojo.Pagination;
import com.ecoolex.framework.common.pojo.Sorting;
import com.ecoolex.framework.common.pojo.Sortings;
import com.ecoolex.framework.common.util.BizAssert;
import com.ecoolex.framework.common.util.Check;
import com.ecoolex.framework.common.util.MessageFormatter;
import com.ecoolex.framework.common.util.Objects;
import com.ecoolex.framework.common.util.crypto.MD5Util;
import com.ecoolex.framework.common.util.io.FileUtil;
import com.ecoolex.framework.modularize.api.service.storage.StorageInfo;
import com.ecoolex.framework.modularize.api.service.storage.StorageService;
import com.ecoolex.framework.modularize.api.service.storage.UploadSource;
import com.ecoolex.framework.modularize.api.service.storage.ZipEntry;
import com.ecoolex.ying.space.core.dao.UserGoodsAttachmentDownloadTaskDAO;
import com.ecoolex.ying.space.core.pojo.AttachmentNode;
import com.ecoolex.ying.space.core.pojo.ChildNode;
import com.ecoolex.ying.space.core.pojo.Node;
import com.ecoolex.ying.space.core.pojo.Root;
import com.ecoolex.ying.space.entity.*;
import com.ecoolex.ying.space.enums.GoodsAttachmentType;
import com.ecoolex.ying.space.enums.UserGoodsAttachmentDownloadTaskStatus;
import com.ecoolex.ying.space.enums.UserGoodsAttachmentDownloadTaskType;
import com.ecoolex.ying.space.pojo.QueryUserGoodsAttachmentDownloadTaskDTO;
import com.google.common.collect.Lists;
import org.apache.catalina.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.time.Instant;
import java.util.Date;
import java.util.List;

import static com.ecoolex.ying.space.entity.UserGoodsAttachmentDownloadTask.*;
import static com.ecoolex.ying.space.entity.UserGoodsAttachmentDownloadTask.NOT_DELETED;
import static com.ecoolex.ying.space.enums.UserGoodsAttachmentDownloadTaskType.*;

@Service
public class UserGoodsAttachmentDownloadTaskManager {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    private static final UploadSource SOURCE = new UploadSource("UserGoodsAttachmentDownloadTask", "ZIP");

    @Autowired
    private UserGoodsAttachmentDownloadTaskDAO dao;

    @Autowired
    private GoodsAttachmentManager goodsAttachmentManager;

    @Autowired
    private DesignerProjectItemsProductManager designerProjectItemsProductManager;

    @Autowired
    private GoodsManager goodsManager;

    @Autowired
    private StorageService storageService;


    public UserGoodsAttachmentDownloadTask createByGoods(Integer userId, Goods goods, Integer goodsAttachmentType, String createUser) {
        GoodsAttachmentType attachmentType = GoodsAttachmentType.realValueOf(goodsAttachmentType);
        UserGoodsAttachmentDownloadTaskType type = valueOf(Group.GOODS, attachmentType);
        BizAssert.isTrue(Check.notNull(type), ResultCode.VERIFY.build(10), "不支持的下载类型");

        // 创建需要生成的数据
        Root root = new Root();
        root.setRoot(createGoodsNode(goods, type));
        // 创建任务
        return create(userId, goods.getName(), type, root, createUser);
    }

    public UserGoodsAttachmentDownloadTask createByProject(Integer userId, DesignerProject project, Integer goodsAttachmentType, String createUser) {
        GoodsAttachmentType attachmentType = GoodsAttachmentType.realValueOf(goodsAttachmentType);
        UserGoodsAttachmentDownloadTaskType type = valueOf(Group.PROJECT, attachmentType);
        BizAssert.isTrue(Check.notNull(type), ResultCode.VERIFY.build(10), "不支持的下载类型");

        Root root = new Root();
        root.setRoot(new ChildNode(MessageFormatter.arrayFormat("P{}-{}", project.getProjectId(), project.getName())));

        goodsManager.queryByDesignerProjectId(project.getProjectId()).forEach(goods -> root.getRoot().addChildList(createGoodsNode(goods, type)));
        // 创建任务
        return create(userId, project.getName(), type, root, createUser);
    }

    public Pagination<UserGoodsAttachmentDownloadTask> queryByUserId(Integer userId, QueryUserGoodsAttachmentDownloadTaskDTO query, Sorting[] sorts, Page page) {
        UserGoodsAttachmentDownloadTaskExample example = new UserGoodsAttachmentDownloadTaskExample();
        UserGoodsAttachmentDownloadTaskExample.Criteria criteria = example.createCriteria();

        if (Check.notNull(query.getType())) {
            criteria.andTypeEqualTo(query.getType());
        }

        criteria.andUserIdEqualTo(userId).andDeletedEqualTo(NOT_DELETED);

        example.orderBy(Sortings.builder(sorts).verify(Column::valueOf).toSql());

        example.setOffset(page.getFirstIndex());
        example.setRows(page.getPageSize());

        return Pagination.create(page, dao.countByExample(example), () -> dao.selectByExample(example));
    }

    public List<UserGoodsAttachmentDownloadTask> queryByUserId(Integer userId, Integer maxTaskId, Integer size) {
        if (size == null || size < 1 || size > 50) {
            return null;
        }
        UserGoodsAttachmentDownloadTaskExample example = new UserGoodsAttachmentDownloadTaskExample();
        UserGoodsAttachmentDownloadTaskExample.Criteria criteria = example.createCriteria();

        criteria.andUserIdEqualTo(userId).andDeletedEqualTo(NOT_DELETED);
        if (Check.notNull(maxTaskId)) {
            criteria.andIdLessThan(maxTaskId);
        }

        example.orderBy(Column.id.desc());
        example.setRows(size);

        return dao.selectByExample(example);
    }

    public void delete(Integer userId, Integer taskId, String updateUser) {
        UserGoodsAttachmentDownloadTask record = new UserGoodsAttachmentDownloadTask();
        record.setDeleted(IS_DELETED);
        record.setUpdateUser(updateUser);
        record.setUpdateTime(Date.from(Instant.now()));

        UserGoodsAttachmentDownloadTaskExample example = new UserGoodsAttachmentDownloadTaskExample();
        example.createCriteria()
                .andIdEqualTo(taskId)
                .andUserIdEqualTo(userId)
                .andDeletedEqualTo(NOT_DELETED);

        int row = dao.updateByExampleSelective(record, example,
                Column.deleted,
                Column.updateUser,
                Column.updateTime);

        BizAssert.isTrue(row == 1, "删除下载任务失败");

    }

    private ChildNode createGoodsNode(Goods goods, UserGoodsAttachmentDownloadTaskType type) {
        ChildNode result = new ChildNode(MessageFormatter.arrayFormat("G{}-{}", goods.getGoodsId(), goods.getName()));
        if (Check.isNull(type.getGoodsAttachmentType())) {
            List<GoodsAttachment> attachmentList = goodsAttachmentManager.queryByGoodsId(goods.getGoodsId());
            attachmentList.forEach(attachment -> {
                List<String> storageList = JSON.parseArray(attachment.getStorageIds(), String.class);
                // 为空的不处理
                if (storageList.isEmpty()) {
                    return;
                }
                GoodsAttachmentType attachmentType = GoodsAttachmentType.realValueOf(attachment.getType());
                // 添加类型描述的节点
                ChildNode attachmentNode = new ChildNode(attachmentType.describe());
                result.addChildList(attachmentNode);
                // 添加附件
                storageList.forEach(storageId -> attachmentNode.addChildList(new AttachmentNode(storageId)));
            });
        } else {
            // 查询出的附件
            GoodsAttachment attachment = goodsAttachmentManager.getByGoodsIdAndType(goods.getGoodsId(), type.getGoodsAttachmentType().real());
            if (Check.notNull(attachment)) {
                List<String> storageList = JSON.parseArray(attachment.getStorageIds(), String.class);
                storageList.forEach(storageId -> result.addChildList(new AttachmentNode(storageId)));
            }
        }
        return result;
    }

    private UserGoodsAttachmentDownloadTask create(Integer userId, String name, UserGoodsAttachmentDownloadTaskType type, Root root, String createUser) {
        BizAssert.isTrue(root.attachmentNumber() > 0, ResultCode.VERIFY.build(20), "打包下载任务不存在附件, 取消任务");
        // 数据MD5值
        String dataMd5 = root.md5();
        // 根据md5查询一下
        UserGoodsAttachmentDownloadTask md5 = getByMD5(type, dataMd5);
        if (Check.notNull(md5) && UserGoodsAttachmentDownloadTaskStatus.PROCESSED.equal(md5.getStatus())) {
            // 如果任务是自己的, 并且没有删除的任务, 直接返回
            if (md5.getUserId().equals(userId) && !md5.getDeleted()) {
                return md5;
            } else {
                // 如果是别人的,或者被删除了,则复制一份出来
                return copy(userId, name, md5, createUser);
            }
        }

        UserGoodsAttachmentDownloadTask record = new UserGoodsAttachmentDownloadTask();
        record.setUserId(userId);
        record.setName(name);
        record.setType(type.real());
        record.setDataMD5(dataMd5);
        record.setData(Objects.serializationToBase64(root));
        record.setStatus(UserGoodsAttachmentDownloadTaskStatus.UNTREATED.real());
        record.setDeleted(NOT_DELETED);
        record.setCreateUser(createUser);
        record.setCreateTime(Date.from(Instant.now()));
        record.setUpdateUser(createUser);
        record.setUpdateTime(Date.from(Instant.now()));
        dao.insert(record);
        return record;
    }

    private UserGoodsAttachmentDownloadTask copy(Integer userId, String name, UserGoodsAttachmentDownloadTask md5Old, String createUser) {
        md5Old.setUserId(userId);
        md5Old.setName(name);
        md5Old.setDeleted(NOT_DELETED);
        md5Old.setCreateUser(createUser);
        md5Old.setCreateTime(Date.from(Instant.now()));
        md5Old.setUpdateUser(createUser);
        md5Old.setUpdateTime(Date.from(Instant.now()));
        dao.insert(md5Old);
        return md5Old;
    }

    private UserGoodsAttachmentDownloadTask getByMD5(UserGoodsAttachmentDownloadTaskType type, String md5) {
        UserGoodsAttachmentDownloadTaskExample example = new UserGoodsAttachmentDownloadTaskExample();
        example.createCriteria()
                .andTypeEqualTo(type.real())
                .andDataMD5EqualTo(md5);
        return dao.selectOneByExampleWithBLOBs(example);
    }

    private UserGoodsAttachmentDownloadTask getProcessedByMD5(UserGoodsAttachmentDownloadTaskType type, String md5) {
        UserGoodsAttachmentDownloadTaskExample example = new UserGoodsAttachmentDownloadTaskExample();
        example.createCriteria()
                .andTypeEqualTo(type.real())
                .andStatusEqualTo(UserGoodsAttachmentDownloadTaskStatus.PROCESSED.real())
                .andDataMD5EqualTo(md5);
        return dao.selectOneByExampleWithBLOBs(example);
    }

    public UserGoodsAttachmentDownloadTask getUntreated() {
        UserGoodsAttachmentDownloadTaskExample example = new UserGoodsAttachmentDownloadTaskExample();
        example.createCriteria()
                .andStatusEqualTo(UserGoodsAttachmentDownloadTaskStatus.UNTREATED.real())
                .andDeletedEqualTo(NOT_DELETED);

        return dao.selectOneByExampleWithBLOBs(example);
    }


    public void processing(UserGoodsAttachmentDownloadTask task, String updateUser) {
        UserGoodsAttachmentDownloadTask old = getProcessedByMD5(UserGoodsAttachmentDownloadTaskType.realValueOf(task.getType()), task.getDataMD5());
        if (Check.notNull(old) && UserGoodsAttachmentDownloadTaskStatus.PROCESSED.equal(old.getStatus())) {
            // 如果md5的文件已处理则更新所有md5的数据
            updateProcessed(old, updateUser);
        } else {
            try {
                Root root = Objects.deserializationForBase64(task.getData());

                List<ZipEntry> result = Lists.newArrayList();
                forEachNode(result, root.getRoot(), "");
                StorageInfo info = storageService.packageZip(task.getName() + ".zip", result, SOURCE);
                // 设置文件
                task.setDownloadStorageId(info.getStorageId());
                updateProcessed(task, updateUser);
            } catch (Exception e) {
                log.error("压缩文件异常. taskId={}", task.getId(), e);
                updateError(task, updateUser);
            }
        }
    }

    private void forEachNode(final List<ZipEntry> result, Node node, String path) {
        if (node instanceof ChildNode) {
            ((ChildNode) node).getChildList().forEach(childNode -> {
                forEachNode(result, childNode, path + "/" + ((ChildNode) node).getName());
            });
        } else if (node instanceof AttachmentNode) {
            result.add(new ZipEntry(path + "/${fileName}", ((AttachmentNode) node).getAttachmentId()));
        }
    }

    private void updateError(UserGoodsAttachmentDownloadTask record, String updateUser) {
        record.setStatus(UserGoodsAttachmentDownloadTaskStatus.ERROR.real());
        record.setUpdateUser(updateUser);
        record.setUpdateTime(Date.from(Instant.now()));

        dao.updateByPrimaryKeySelective(record,
                Column.status,
                Column.updateUser,
                Column.updateTime);
    }

    private void updateProcessed(UserGoodsAttachmentDownloadTask record, String updateUser) {
        record.setStatus(UserGoodsAttachmentDownloadTaskStatus.PROCESSED.real());
        record.setUpdateUser(updateUser);
        record.setUpdateTime(Date.from(Instant.now()));

        UserGoodsAttachmentDownloadTaskExample example = new UserGoodsAttachmentDownloadTaskExample();
        example.createCriteria()
                .andStatusEqualTo(UserGoodsAttachmentDownloadTaskStatus.UNTREATED.real())
                .andTypeEqualTo(record.getType())
                .andDataMD5EqualTo(record.getDataMD5())
                .andDeletedEqualTo(NOT_DELETED);

        dao.updateByExampleSelective(record,
                example,
                Column.downloadStorageId,
                Column.status,
                Column.updateUser,
                Column.updateTime);
    }
}